
package ija.net;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;



/**
 * Trida se stara o prijimani zprav v peer-to-peer komunikaci. Je ralizovana,
 * aby vzdy bylo zaruceno, ze zpravy budou prijimany na stejnem portu a ten
 * bude zabran po celou dobu komunikace.
 * @author Radek Necas, xnecas18
 */
public class Server extends Thread {

    protected ServerSocket serverSocket;
    protected Socket clientSocket;
    protected int localPort;
    protected Networker netController;
    protected List<ServerEventListener> listeners;
    
    
    /**
     * Konstruktor vytvori serverovou cast peer-to-peer komunikace.
     * @param port port na kterem bude server naslouchat
     * @param listener objekt, kteremu bude server zasilat udalosti o prijatych zpravach
     */
    public Server(int port, ServerEventListener listener) {
        this.localPort = port;
        try {
            this.serverSocket = new ServerSocket(port);
        } catch(IOException e) {
            System.err.println("Nelze se pripojit na port" + e.getLocalizedMessage());
        }
        this.clientSocket = null;
        this.listeners = new ArrayList<>();
        this.listeners.add(listener);
    }
    
    /**
     * Metoda prida listenera
     * @param listener objekt, ktery bude informovan o prijatych zpravach
     */
    public synchronized void addListener(ServerEventListener listener) {
        listeners.add(listener);
    }
    
    /**
     * Metoda odstrani listenera
     * @param listener listener, ktery bude odebran
     */
    public synchronized void removeListener(ServerEventListener listener) {
        listeners.remove(listener);
    }
    
    /**
     * Metoda realizuje beh serveru. Ten ceka v nekonecne smycce
     */
    @Override
    public void run() {
        clientSocket = null;
        ObjectInputStream inObj = null;
        Object o;
        Message m;
        
        boolean end = false;
        while(!end) {
            try {
                clientSocket = serverSocket.accept();
                inObj = new ObjectInputStream(clientSocket.getInputStream());    
                o = inObj.readObject();
                if(o instanceof Message) {
                    m = (Message)o;
                    if(m.getType() == MessageType.ENDSERVER) {
                        System.out.println("Server: Zpracovavam zpravu endserver");
                        end = true;
                    }
                    else {
                        if(m.getType() == MessageType.ASKFORCONN) {
                            String s = clientSocket.getInetAddress().toString().substring(1) + ":" + m.getDetails();
                            m.setDetails(s);
                        }
                        // while(!activ)
                        fireEvent(m);
                        // deactivate
                    }
                }
                else {
                    // FireEvent networker ERRMSG s popisem
                    fireEvent(new Message(MessageType.SERVERERR, "Bad message type"));
                }
                
            } catch(IOException | ClassNotFoundException e) {
                fireEvent(new Message(MessageType.SERVERERR, e.getMessage()));
            
            } finally {
                if(inObj != null)
                    try {
                    inObj.close();
                } catch (IOException ex) {
                    //Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
                }
                if(clientSocket != null) {
                    try {
                        clientSocket.close();
                        clientSocket = null;
                        inObj.close();
                    } catch (IOException ex) {
                        clientSocket = null;
                    }
                }
            } //end finally
        } // end while
        
        try {
            System.out.println("Rusim serverSocket");
            serverSocket.close();
            serverSocket = null;
        } catch (IOException ex) {
            serverSocket = null;
        }
    }
    
    public ServerSocket getServerSocket() {
        return serverSocket;
    }
    
    private synchronized void fireEvent(Message msg) {
        for(ServerEventListener l: listeners) {
            l.serveMessage(msg);
        }
    }
}
